/*
 * File Name：AsyncDownload.java
 * Copyright：Copyright 2008 - 2013 CiWong.Inc. All Rights Reserved.
 * Description： AsyncDownload.java
 * Modify By：PLA-ZJLIU
 * Modify Date：2012-11-3
 * Modify Type：Add
 */
package com.ciwong.libs.utils.volley;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.methods.GetMethod;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.ciwong.libs.utils.CWLog;
import com.ciwong.libs.utils.CWSys;
import com.ciwong.libs.utils.FileUtils;
import com.ciwong.libs.utils.NetworkUtils;
import com.ciwong.libs.utils.StringUtils;

/**
 * 下载工具类<br />
 * 在下载过程中会发送下载进度广播
 * 
 * @author PLA-ZJLIU
 * @version ciwong v.1.0 2012-11-3
 * @since ciwong v.1.0
 */
@SuppressWarnings("unused")
public class AsyncDownload
{
    private static final String TAG = "download";

    /**
     * 下载过程中,若超过了设置时间没有动(即下载的大小不变),则视为下载超时.单位秒
     */
    private static int DOWNLOADING_TIME_OUT = 60;

    /**
     * 下载允许并发数量
     */
    public static int MAX_DOWNLOAD_COUNT = 2;

    /**
     * 下载出错
     */
    private static final int DOWNLOAD_ERROR = -1;

    /**
     * 正在下载
     */
    private static final int DOWNLOAD_DOWNLOADING = 0;

    /**
     * 已经下载完成了
     */
    private static final int DOWNLOAD_COMPLETED = 1;

    /**
     * 下载完成
     */
    private static final int DOWNLOAD_COMPLETE = 2;

    /**
     * 下载暂停
     */
    private static final int DOWNLOAD_PUASE = 3;

    /**
     * 下载停止
     */
    private static final int DOWNLOAD_STOP = 4;

    /**
     * 开始下载
     */
    private static final int DOWNLOAD_START = 5;

    /**
     * 下载状态：开始下载
     */
    public static final int DOWNLOAD_STATUS_START = 0;

    /**
     * 下载状态：暂停
     */
    public static final int DOWNLOAD_STATUS_PAUSE = 1;

    /**
     * 下载状态：暂停后重新下载
     */
    public static final int DOWNLOAD_STATUS_RESUME = 4;

    /**
     * 下载状态：停止下载
     */
    public static final int DOWNLOAD_STATUS_STOP = 2;

    /**
     * 下载状态：取消
     */
    public static final int DOWNLOAD_STATUS_CANCEL = 3;

    /**
     * 下载状态：正在下载
     */
    public static final int DOWNLOAD_STATUS_DOWNLOADING = 4;

    /**
     * 下载状态：等待下载
     */
    public static final int DOWNLOAD_STATUS_WAIT = 5;

    /**
     * 下载状态：下载已完成(所有的下载已完成,调用者去判断是否是错误的状态)
     */
    public static final int DOWNLOAD_STATUS_ALL_COMPLETE = 6;

    /**
     * 下载状态：下载已完成(等待的任务已完成,调用者去判断是否是错误的状态)
     */
    public static final int DOWNLOAD_STATUS_WAIT_COMPLETE = 7;

    /**
     * 下载状态：下载出错
     */
    public static final int DOWNLOAD_STATUS_ERROR = 8;

    /**
     * 下载状态：下载任务已经存在
     */
    public static final int DOWNLOAD_STATUS_TASK_EXISTS = 9;

    /**
     * 下载出错:网络未连接
     */
    public static final int ERROR_TYPE_NET_DISABLE = 0;

    /**
     * 下载出错:下载地址不正确
     */
    public static final int ERROR_TYPE_URL = 1;

    /**
     * 下载出错:下载失败
     */
    public static final int ERROR_TYPE_FAILURE = 2;

    /**
     * 下载优先级高
     */
    public static final int PRIORITY_HIGHT = 1;

    /**
     * 下载优先级中
     */
    public static final int PRIORITY_MIDDLE = 0;

    /**
     * 下载优先级低
     */
    public static final int PRIORITY_LOW = -1;

    private static AsyncDownload mDownload;

    private Map<Object, DownloadThread> downloadThreads;

    private List<DownloadThread> downloadThreadList;

    private List<DownloadThread> downloading;

    private List<DownloadThread> waitThreads;

    private Object async = new Object();

    private Object puaseAsync = new Object();

    private boolean isStop;

    private Thread mainThread;

    private Map<String, String> headers;

    private List<Callback> mCallbacks;

    private Handler mHandler = new Handler()
    {
        public void handleMessage(android.os.Message msg)
        {
            DownloadTask task = (DownloadTask) msg.obj;
            final List<OnProgressUpdate> mUpdates = task.getOnProgressUpdate();
            long total = task.getTotalSize() / 1024;
            long progress = task.getDownSize() / 1024;
            switch (msg.what)
            {
                case DOWNLOAD_START:
                    if (mUpdates != null)
                    {
                        for (OnProgressUpdate mUpdate : mUpdates)
                        {
                            if (mUpdate != null)
                            {
                                mUpdate.start(task, progress, total);
                            }
                        }
                    }
                    if (mCallbacks != null)
                    {
                        for (AsyncDownload.Callback mCall : mCallbacks)
                        {
                            if (mCall != null)
                            {
                                mCall.start(task, progress, total);
                            }
                        }
                    }
                    break;
                case DOWNLOAD_DOWNLOADING:
                    if (mUpdates != null)
                    {
                        for (OnProgressUpdate mUpdate : mUpdates)
                        {
                            if (mUpdate != null)
                            {
                                mUpdate.progressUpdate(total, progress,
                                        task.getTag());
                            }
                        }
                    }
                    if (mCallbacks != null)
                    {
                        for (AsyncDownload.Callback mCall : mCallbacks)
                        {
                            if (mCall != null)
                            {
                                mCall.progressUpdate(total, progress,
                                        task.getTag());
                            }
                        }
                    }
                    break;
                case DOWNLOAD_COMPLETE:
                    if (mUpdates != null)
                    {
                        for (OnProgressUpdate mUpdate : mUpdates)
                        {
                            if (mUpdate != null)
                            {
                                mUpdate.complete(task);
                                mUpdate.complete(total, task.getTag());
                            }
                        }
                    }
                    if (mCallbacks != null)
                    {
                        for (AsyncDownload.Callback mCall : mCallbacks)
                        {
                            if (mCall != null)
                            {
                                mCall.complete(task);
                                mCall.complete(total, task.getTag());
                            }
                        }
                    }
                    break;
                case DOWNLOAD_PUASE:
                    break;
                case DOWNLOAD_STOP:
                    break;
                case DOWNLOAD_ERROR:
                    if (mUpdates != null)
                    {
                        for (OnProgressUpdate mUpdate : mUpdates)
                        {
                            if (mUpdate != null)
                            {
                                mUpdate.error(msg.arg1, task);
                            }
                        }
                    }
                    if (mCallbacks != null)
                    {
                        for (AsyncDownload.Callback mCall : mCallbacks)
                        {
                            if (mCall != null)
                            {
                                mCall.error(msg.arg1, task);
                            }
                        }
                    }
                    break;
                case DOWNLOAD_COMPLETED:
                    CWLog.d("debug", "tag:" + task.getTag() + "     已经下载完");
                    if (mUpdates != null)
                    {
                        for (OnProgressUpdate mUpdate : mUpdates)
                        {
                            if (mUpdate != null)
                            {
                                mUpdate.completed(task);
                            }
                        }
                    }
                    if (mCallbacks != null)
                    {
                        for (AsyncDownload.Callback mCall : mCallbacks)
                        {
                            if (mCall != null)
                            {
                                mCall.completed(task);
                            }
                        }
                    }
                    break;
            }
            if (msg.what == DOWNLOAD_COMPLETE)
            {
                Log.e(TAG, "下载完成");
            }
        };
    };

    /**
     * 设置上下文
     * 
     * @param mContext
     *            上下文 soc.io
     */
    public static void setContext(Context mContext)
    {

    }

    /**
     * 获取下载实例
     * 
     * @return 下载实例
     */
    public static AsyncDownload getInstance()
    {
        if (mDownload == null)
        {
            mDownload = new AsyncDownload();
        }
        return mDownload;
    }

    /**
     * 取消下载(默认为删除下载的文件)
     * 
     * @param tag
     *            唯一标识
     */
    public void cancel(Object tag)
    {
        remove(tag, true);
    }

    /**
     * 取消下载
     * 
     * @param tag
     *            唯一标识
     * @param delFile
     *            是否删除文件
     */
    public void cancel(Object tag, boolean delFile)
    {
        remove(tag, delFile);
    }

    /**
     * 构造函数
     * 
     * @param
     *            上下文
     */
    private AsyncDownload()
    {
        downloading = new ArrayList<AsyncDownload.DownloadThread>();
        waitThreads = new ArrayList<AsyncDownload.DownloadThread>();
    }

    /**
     * 设置每一次请求的超时时间(一个请求任务在指定时间内没有下载数据则视为超时)
     * 
     * @param timeOut
     *            超时时间
     */
    public static void setDownloadTimeOut(int timeOut)
    {
        DOWNLOADING_TIME_OUT = timeOut;
    }

    /**
     * 设置最大并发下载数量,最大值不能超过5个
     * 
     * @param count
     *            最大并发下载数量
     */
    public static void setMaxDownloadCount(int count)
    {
        MAX_DOWNLOAD_COUNT = count > 5 ? 5 : count;
    }

    /**
     * 设置请求头
     * 
     * @param header
     */
    public void setHeaders(Map<String, String> header)
    {
        this.headers = header;
    }

    /**
     * 添加下载任务
     * 
     * @param url
     *            下载地址
     * @param mOnProgressUpdate
     */
    private DownloadTask addTask(String url, Map<String, String> params,
            OnProgressUpdate mOnProgressUpdate, Object tag)
    {
        DownloadTask task = new DownloadTask();
        task.setDownloadUrl(url);
        task.setParams(params);
        task.setTag(tag);
        if (mOnProgressUpdate != null)
        {
            task.getOnProgressUpdate().add(mOnProgressUpdate);
        }
        return task;
    }

    /**
     * 
     * 添加下载任务
     * 
     * @param url
     *            下载地址
     * @param params
     *            参数
     * @param path
     *            保存文件路径
     * @param mOnProgressUpdate
     *            更新进度条
     * @param tag
     *            唯一标识
     * @return 下载状态<br />
     *         {@link #DOWNLOAD_STATUS_START}<br />
     *         {@link #DOWNLOAD_STATUS_ALL_COMPLETE}<br />
     *         {@link #DOWNLOAD_STATUS_CANCEL}<br />
     *         {@link #DOWNLOAD_STATUS_DOWNLOADING}<br />
     *         {@link #DOWNLOAD_STATUS_PAUSE}<br />
     *         {@link #DOWNLOAD_STATUS_RESUME}<br />
     *         {@link #DOWNLOAD_STATUS_STOP}<br />
     *         {@link #DOWNLOAD_STATUS_WAIT}<br />
     *         {@link #DOWNLOAD_STATUS_WAIT_COMPLETE}<br />
     *         {@link #DOWNLOAD_STATUS_ERROR}<br />
     */
    public int addTask(String url, Map<String, String> params, String path,
            OnProgressUpdate mOnProgressUpdate, Object tag)
    {
        return addTask(url, params, path, mOnProgressUpdate, tag,
                PRIORITY_MIDDLE);
    }

    /**
     * 
     * 添加下载任务
     * 
     * @param url
     *            下载地址
     * @param params
     *            参数
     * @param path
     *            保存文件路径
     * @param mOnProgressUpdate
     *            更新进度条
     * @param tag
     *            唯一标识
     * @param priority
     *            下载优先级
     * @return 下载状态<br />
     *         {@link #DOWNLOAD_STATUS_START}<br />
     *         {@link #DOWNLOAD_STATUS_ALL_COMPLETE}<br />
     *         {@link #DOWNLOAD_STATUS_CANCEL}<br />
     *         {@link #DOWNLOAD_STATUS_DOWNLOADING}<br />
     *         {@link #DOWNLOAD_STATUS_PAUSE}<br />
     *         {@link #DOWNLOAD_STATUS_RESUME}<br />
     *         {@link #DOWNLOAD_STATUS_STOP}<br />
     *         {@link #DOWNLOAD_STATUS_WAIT}<br />
     *         {@link #DOWNLOAD_STATUS_WAIT_COMPLETE}<br />
     *         {@link #DOWNLOAD_STATUS_ERROR}<br />
     */
    public int addTask(String url, Map<String, String> params, String path,
            OnProgressUpdate mOnProgressUpdate, Object tag, int priority)
    {
        DownloadTask task = addTask(url, params, mOnProgressUpdate, tag);
        task.setSavePath(path);
        task.setPriority(priority);
        return addTask(task);
    }

    /**
     * 获取优级为中的任务在对列中的最后索引
     * 
     * @return 索引
     */
    private int getMiddleIndex()
    {
        int index = 0;
        synchronized (async)
        {
            int len = downloadThreadList.size();
            for (int i = len - 1; i >= 0; i--)
            {
                final int priority = downloadThreadList.get(i).getPriority();
                if (priority == PRIORITY_MIDDLE || priority == PRIORITY_HIGHT)
                {
                    index = i;
                    break;
                }
            }
        }
        return index;
    }

    /**
     * 设置线程优先级
     * 
     * @param task
     *            下载任务
     * @param thread
     *            下载线程
     */
    private void setPriority(DownloadTask task, DownloadThread thread)
    {
        if (task.getPriority() == PRIORITY_HIGHT)
        {
            downloadThreadList.add(0, thread);
        }
        else if (task.getPriority() == PRIORITY_MIDDLE)
        {
            final int middleIndex = getMiddleIndex();
            downloadThreadList.add(middleIndex, thread);
        }
        else
        {
            downloadThreadList.add(thread);
        }
    }

    /**
     * 添加下载任务
     * 
     * @param task
     *            DownloadTask
     * @return 下载状态<br />
     *         {@link #DOWNLOAD_STATUS_START}<br />
     *         {@link #DOWNLOAD_STATUS_ALL_COMPLETE}<br />
     *         {@link #DOWNLOAD_STATUS_CANCEL}<br />
     *         {@link #DOWNLOAD_STATUS_DOWNLOADING}<br />
     *         {@link #DOWNLOAD_STATUS_PAUSE}<br />
     *         {@link #DOWNLOAD_STATUS_RESUME}<br />
     *         {@link #DOWNLOAD_STATUS_STOP}<br />
     *         {@link #DOWNLOAD_STATUS_WAIT}<br />
     *         {@link #DOWNLOAD_STATUS_WAIT_COMPLETE}<br />
     *         {@link #DOWNLOAD_STATUS_ERROR}<br />
     */
    public int addTask(DownloadTask task)
    {
        if (downloadThreads != null
                && downloadThreads.containsKey(task.getTag()))
        {
            DownloadThread existsThread = downloadThreads.get(task.getTag());
            if (task.getOnProgressUpdate() != null)
            {
                existsThread.getTask().getOnProgressUpdate()
                        .addAll(task.getOnProgressUpdate());
            }
            if (task.getPriority() != existsThread.getPriority()
                    && downloadThreadList.contains(existsThread))
            {
                existsThread.setPriority(task.getPriority());
                downloadThreadList.remove(existsThread);
                setPriority(task, existsThread);
            }
            return existsThread.status;
        }
        final DownloadThread thread = new DownloadThread();
        thread.setTask(task);
        thread.setPriority(task.getPriority());
        if (downloadThreads == null)
        {
            downloadThreads = new HashMap<Object, AsyncDownload.DownloadThread>();
        }
        if (downloadThreadList == null)
        {
            downloadThreadList = new ArrayList<AsyncDownload.DownloadThread>();
        }
        thread.setThreadTag(task.getTag());
        setPriority(task, thread);
        CWLog.d(TAG, "添加新任务");
        downloadThreads.put(thread.getThreadTag(), thread);
        if (mainThread == null)
        {
            mainThread = new Thread()
            {
                @Override
                public void run()
                {
                    boolean stop = false;
                    while (!stop)
                    {
                        boolean isNone = false;
                        synchronized (async)
                        {
                            isNone = downloadThreads == null
                                    || downloadThreads.isEmpty();
                        }
                        if (!isNone)
                        {
                            int size = 0;
                            boolean isContinue = false;
                            for (int i = 0; i < MAX_DOWNLOAD_COUNT; i++)
                            {
                                isContinue = false;
                                DownloadThread thread = null;
                                synchronized (async)
                                {
                                    size = downloading.size();
                                    if (size <= i)
                                    {
                                        thread = downloadThreadList == null
                                                || downloadThreadList.isEmpty() ? null
                                                : downloadThreadList.remove(0);
                                        if (thread != null)
                                        {
                                            downloading.add(thread);
                                        }
                                    }
                                    else
                                    {
                                        thread = downloading.get(i);
                                    }
                                }
                                if (thread == null)
                                {
                                    continue;
                                }
                                CWLog.e(TAG, "tag:" + thread.getTask().getTag());
                                final DownloadTask task = thread.getTask();
                                if (isStop)
                                {
                                    thread.stop();
                                    thread.status = DOWNLOAD_STATUS_STOP;
                                    File f = new File(task.getSavePath());
                                    f.delete();
                                }
                                else
                                {
                                    if (thread.isAlive())
                                    {
                                        thread.status = DOWNLOAD_STATUS_DOWNLOADING;
                                        if (thread.isWaited())
                                        {
                                            synchronized (thread)
                                            {
                                                thread.notify();
                                            }
                                            thread.isWaited = false;
                                            CWLog.i(TAG, "notify");
                                        }
                                        int what = DOWNLOAD_DOWNLOADING;
                                        CWLog.d(TAG,
                                                "seconds:"
                                                        + thread.seconds
                                                        + "    cur:"
                                                        + (task.getDownSize() / 1024)
                                                        + "    last:"
                                                        + (task.getLastSize() / 1024)
                                                        + "    total:"
                                                        + (task.getTotalSize() / 1024));
                                        if (task.getDownSize() == task
                                                .getLastSize())
                                        {
                                            thread.addSecond(1);
                                        }
                                        else
                                        {
                                            thread.resetSecond();
                                        }
                                        task.setLastSize(task.getDownSize());
                                        if (thread.seconds / 2 > DOWNLOADING_TIME_OUT)
                                        {
                                            thread.resetSecond();
                                            thread.stop();
                                            thread.addRequestCount();
                                            if (thread.getRequestCount() >= DownloadThread.MAX_REQUEST_COUNT)
                                            {
                                                what = DOWNLOAD_ERROR;
                                                removeDownloading(thread);
                                                remove(thread);
                                            }
                                        }
                                        Message msg = mHandler
                                                .obtainMessage(what);
                                        msg.obj = task;
                                        if (what == DOWNLOAD_ERROR)
                                        {
                                            msg.arg1 = ERROR_TYPE_FAILURE;
                                        }
                                        msg.sendToTarget();
                                    }
                                    else
                                    {
                                        thread.getThread().start();
                                    }
                                }
                            }
                        }
                        else
                        {
                            break;
                        }
                        try
                        {
                            Thread.sleep(500);
                        }
                        catch (InterruptedException e)
                        {
                            e.printStackTrace();
                        }
                    }
                    mainThread = null;
                }
            };
            mainThread.start();
        }
        int downloadingSize = 0;
        int downloadSize = 0;
        synchronized (async)
        {
            downloadingSize = downloading.size();
            downloadSize = downloadThreadList.size();
        }
        if (downloadingSize + downloadSize <= MAX_DOWNLOAD_COUNT)
        {
            thread.status = DOWNLOAD_STATUS_DOWNLOADING;
            return DOWNLOAD_STATUS_DOWNLOADING;
        }
        thread.status = DOWNLOAD_STATUS_WAIT;
        return DOWNLOAD_STATUS_WAIT;
    }

    private void remove(DownloadThread thread)
    {
        synchronized (async)
        {
            downloadThreads.remove(thread.getThreadTag());
            // downloadThreadList.remove(thread);
            CWLog.d(TAG, "remove:" + thread);
        }
    }

    private void add(DownloadThread thread)
    {
        synchronized (async)
        {
            downloadThreads.put(thread.getThreadTag(), thread);
            downloadThreadList.add(thread);
        }
    }

    @SuppressWarnings("deprecation")
    private void run(DownloadThread thread)
    {
        final DownloadTask task = thread.getTask();
        boolean reRequest = false;// 是否重新请求
        if (task != null)
        {
            if (new File(task.getSavePath()).exists())
            {
                Message msg = mHandler.obtainMessage(DOWNLOAD_COMPLETED);
                msg.obj = task;
                msg.sendToTarget();
                remove(thread);
                removeDownloading(thread);
                return;
            }
            if (!NetworkUtils.isOnline())
            {
                CWLog.e(TAG, "下载出错:无网络连接");
                reRequest = isReRequest(thread, task, ERROR_TYPE_NET_DISABLE);
                try
                {
                    Thread.sleep(10000);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
            else
            {
                boolean isStop = false;
                synchronized (async)
                {
                    isStop = task.isStop();
                }
                if (!isStop)
                {
                    GetMethod getRequest = null;
                    try
                    {
                        getRequest = HttpHelper.getGetConnection(null,
                                task.getDownloadUrl(), headers);
                    }
                    catch (Exception e1)
                    {
                        e1.printStackTrace();
                        reRequest = isReRequest(thread, task, ERROR_TYPE_URL);
                    }
                    if (getRequest != null)
                    {
                        thread.setRequest(getRequest);
                        HttpClient mClient = new HttpClient();
                        long curSize = getCurrDownloadSize(task.getCachePath());
                        // getRequest.addRequestHeader("User-Agent", "NetFox");
                        getRequest.addRequestHeader("Range", "bytes=" + curSize
                                + "-");
                        mClient.setConnectionTimeout(6000);
                        RandomAccessFile out = null;
                        try
                        {
                            mClient.executeMethod(getRequest);
                            task.setRunning();
                            int resultCode = getRequest.getStatusCode();
                            int what = DOWNLOAD_COMPLETE;
                            if (resultCode == 206 || resultCode == 200)
                            {
                                long totalSize = getRequest
                                        .getResponseContentLength() + curSize;

                                InputStream in = getRequest
                                        .getResponseBodyAsStream();
                                if (task.getCachePath() != null)
                                {
                                    out = new RandomAccessFile(new File(
                                            task.getCachePath()), "rwd");
                                    task.setTotalSize(totalSize);
                                    Message startMsg = mHandler
                                            .obtainMessage(DOWNLOAD_START);
                                    startMsg.obj = task;
                                    startMsg.sendToTarget();
                                    int len = 0;
                                    int bufferSize = 1024 * 2;
                                    out.seek(curSize);
                                    byte[] buffer = new byte[bufferSize];
                                    long size = curSize;
                                    boolean temp = false;
                                    while ((len = in.read(buffer)) > 0)
                                    {
                                        out.write(buffer, 0, len);
                                        size += len;
                                        task.setDownSize(size);
                                        synchronized (puaseAsync)
                                        {
                                            temp = thread.isPause();
                                        }
                                        if (temp)
                                        {
                                            task.pause();
                                            what = DOWNLOAD_PUASE;
                                            synchronized (thread)
                                            {
                                                Message msg = mHandler
                                                        .obtainMessage(what);
                                                msg.obj = task;
                                                msg.sendToTarget();
                                                thread.wait();
                                            }
                                        }
                                        if (thread.isStop())
                                        {
                                            task.stop();
                                            what = DOWNLOAD_STOP;
                                            break;
                                        }
                                    }
                                    if (size == totalSize)
                                    {
                                        what = DOWNLOAD_COMPLETE;
                                    }
                                    if (what == DOWNLOAD_COMPLETE)
                                    {
                                        FileUtils.copyFile(task.getCachePath(),
                                                task.getSavePath());
                                        new File(task.getCachePath()).delete();
                                        in.close();

                                    }
                                    else
                                    {
                                        getRequest.abort();
                                    }
                                    Message msg = mHandler.obtainMessage(what);
                                    msg.obj = task;
                                    msg.sendToTarget();
                                }
                                if (what == DOWNLOAD_STOP
                                        || what == DOWNLOAD_COMPLETE)
                                {
                                    remove(thread);
                                    removeDownloading(thread);
                                }
                            }
                            else if (resultCode == 416)
                            {
                                Message msg = mHandler
                                        .obtainMessage(DOWNLOAD_COMPLETED);
                                task.setTotalSize(curSize);
                                task.setDownSize(curSize);
                                msg.obj = task;
                                msg.sendToTarget();
                                remove(thread);
                                removeDownloading(thread);
                            }
                            else
                            {
                                CWLog.e(TAG,
                                        "tag:" + task.getTag()
                                                + "     下载出错        请求地址:"
                                                + task.getDownloadUrl()
                                                + "    请求返回码:" + resultCode);
                                reRequest = isReRequest(thread, task,
                                        ERROR_TYPE_FAILURE);
                            }
                        }
                        catch (Exception e)
                        {
                            CWLog.e(TAG, "tag:" + task.getTag()
                                    + "     下载捕获异常 :" + e.getMessage()
                                    + "     url:"
                                    + thread.getTask().getDownloadUrl());
                            reRequest = isReRequest(thread, task,
                                    ERROR_TYPE_FAILURE);
                            FileUtils.delete(task.getSavePath());
                            e.printStackTrace();
                        }
                        finally
                        {
                            if (getRequest != null)
                            {
                                getRequest.releaseConnection();
                                getRequest = null;
                            }
                            if (out != null)
                            {
                                try
                                {
                                    out.close();
                                }
                                catch (IOException e)
                                {
                                    e.printStackTrace();
                                }
                            }
                            mClient = null;
                        }
                    }
                }
            }
        }
        CWLog.e(TAG,
                "tag:" + task.getTag() + "      count:"
                        + thread.getRequestCount() + "     任务结束:" + reRequest
                        + ";thread" + thread.isStop);
        if (reRequest)
        {
            run(thread);
            thread.resetSecond();
        }
        synchronized (async)
        {
            thread.relaseThread();
        }

    }

    private boolean isReRequest(DownloadThread thread, DownloadTask task,
            int errorType)
    {
        boolean reRequest = isReRequest(thread);

        if (!reRequest)
        {
            Message msg = mHandler.obtainMessage(DOWNLOAD_ERROR);
            msg.obj = task;
            msg.arg1 = errorType;
            msg.sendToTarget();
            thread.stop();
            remove(thread);
        }
        return reRequest;
    }

    private void removeDownloading(DownloadThread thread)
    {
        synchronized (async)
        {
            downloading.remove(thread);
        }
    }

    private boolean isReRequest(DownloadThread thread)
    {
        boolean reRequest = false;
        CWLog.e(TAG, "requestCount:" + thread.getRequestCount() + "   stop:"
                + thread.isStop);
        boolean temp = thread.getRequestCount() < DownloadThread.MAX_REQUEST_COUNT;
        if (temp && !thread.isStop)
        {
            thread.addRequestCount();
            reRequest = true;
        }
        if (!temp)
        {
            removeDownloading(thread);
            remove(thread);
        }
        return reRequest;
    }

    /**
     * 添加下载监听事件
     * 
     * @param mCallback
     */
    public void addListener(AsyncDownload.Callback mCallback)
    {
        if (mCallbacks == null)
        {
            mCallbacks = new ArrayList<AsyncDownload.Callback>();
        }
        if (mCallback != null)
        {
            mCallbacks.add(mCallback);
        }
    }

    /**
     * 删除下载监听
     * 
     * @param mCallback
     */
    public void removeListener(AsyncDownload.Callback mCallback)
    {
        if (mCallback != null && mCallbacks != null)
        {
            mCallbacks.remove(mCallback);
        }
    }

    /**
     * 读取当前文件下载的长度
     * 
     * @param filePath
     *            文件路径
     * @return 当前长度
     */
    private long getCurrDownloadSize(String filePath)
    {
        long size = 0;
        File file = new File(filePath);
        if (file.exists() && !file.isDirectory())
        {
            size = file.length();
        }
        return size;
    }

    /**
     * 停止下载
     * 
     * @param tag
     *            为添加任务时传入 的TAG
     */
    public DownloadThread stop(Object tag)
    {
        if (downloadThreads == null)
        {
            return null;
        }
        DownloadThread mThread = downloadThreads.get(tag);
        if (mThread == null)
        {
            return null;
        }
        synchronized (async)
        {
            mThread.stop();
            downloading.remove(mThread);
            downloadThreads.remove(tag);
            downloadThreadList.remove(mThread);
        }
        return mThread;
    }

    /**
     * 暂停下载
     * 
     * @param tag
     *            为添加任务时传入 的TAG
     */
    public void puase(Object tag)
    {
        if (downloadThreads == null)
        {
            return;
        }
        DownloadThread mThread = downloadThreads.get(tag);
        if (mThread != null)
        {
            synchronized (puaseAsync)
            {
                mThread.pause();
                mThread.status = DOWNLOAD_STATUS_PAUSE;
                waitThreads.add(mThread);
            }
            synchronized (async)
            {
                removeDownloading(mThread);
            }
        }
    }

    /**
     * 移除下载
     * 
     * @param tag
     *            为添加任务时传入 的TAG
     */
    public void remove(Object tag, boolean delFile)
    {
        DownloadThread mThread = stop(tag);
        if (downloadThreads == null || mThread == null)
        {
            return;
        }
        if (delFile)
        {
            final String path = mThread.getTask().getSavePath();
            if (path != null)
            {
                File f = new File(path);
                if (f.exists())
                {
                    f.delete();
                }
            }
        }
    }

    /**
     * 
     * 恢复下载
     * 
     * @param tag
     *            唯一标识符
     * @return 下载状态<br />
     *         {@link #DOWNLOAD_STATUS_START}<br />
     *         {@link #DOWNLOAD_STATUS_ALL_COMPLETE}<br />
     *         {@link #DOWNLOAD_STATUS_CANCEL}<br />
     *         {@link #DOWNLOAD_STATUS_DOWNLOADING}<br />
     *         {@link #DOWNLOAD_STATUS_PAUSE}<br />
     *         {@link #DOWNLOAD_STATUS_RESUME}<br />
     *         {@link #DOWNLOAD_STATUS_STOP}<br />
     *         {@link #DOWNLOAD_STATUS_WAIT}<br />
     *         {@link #DOWNLOAD_STATUS_WAIT_COMPLETE}<br />
     *         {@link #DOWNLOAD_STATUS_ERROR}<br />
     * 
     */
    public int resume(Object tag)
    {
        if (downloadThreads == null)
        {
            return DOWNLOAD_STATUS_ALL_COMPLETE;
        }
        DownloadThread mThread = downloadThreads.get(tag);
        if (mThread != null)
        {
            mThread.resumse();
            if (waitThreads == null || waitThreads.isEmpty())
            {
                return DOWNLOAD_STATUS_WAIT_COMPLETE;
            }
            int waitCount = waitThreads.size();
            for (DownloadThread waitThread : waitThreads)
            {
                if (waitThread.getTask().getTag() == tag)
                {
                    synchronized (puaseAsync)
                    {
                        waitThreads.remove(waitThread);
                        waitThread.isWaited = true;
                    }
                    int downloadingSize = 0;
                    synchronized (async)
                    {
                        downloading.add(waitThread);
                        downloadingSize = downloading.size();
                    }
                    if (downloadingSize <= MAX_DOWNLOAD_COUNT)
                    {
                        return DOWNLOAD_STATUS_DOWNLOADING;
                    }
                    else
                    {
                        return DOWNLOAD_STATUS_WAIT;
                    }
                }
            }
        }
        return DOWNLOAD_ERROR;
    }

    /**
     * 下载进度监听接口(更新UI)
     * 
     * @author PLA-ZJLIU
     * 
     */
    public static abstract class OnProgressUpdate
    {
        /**
         * 更新下载进度(更新UI)
         * 
         * @param total
         *            总值
         * @param progress
         *            进度值
         */
        public abstract void progressUpdate(long total, long progress,
                Object tag);

        /**
         * 下载完成后调用的方法
         * 
         */
        public void complete(DownloadTask task)
        {

        }

        /**
         * 下载完成后调用的方法
         * 
         */
        public void complete(long total, Object tag)
        {

        }

        /**
         * 下载出错
         * 
         * @param errorType
         * @param task
         */
        public abstract void error(int errorType, DownloadTask task);

        /**
         * 已经下载完成
         * 
         * @param task
         */
        public abstract void completed(DownloadTask task);

        /**
         * 开始下载
         * 
         * @param task
         *            任务
         */
        public void start(DownloadTask task, long curSize, long totalSize)
        {

        }
    }

    public static abstract class Callback
    {

        /**
         * 开始下载
         * 
         * @param task
         *            任务
         */
        public void start(DownloadTask task, long curSize, long totalSize)
        {

        }

        /**
         * 更新下载进度(更新UI)
         * 
         * @param total
         *            总值
         * @param progress
         *            进度值
         */

        public abstract void progressUpdate(long total, long progress,
                Object tag);

        /**
         * 下载完成后调用的方法
         * 
         */
        public void complete(DownloadTask task)
        {

        }

        /**
         * 下载完成后调用的方法
         * 
         */
        public void complete(long total, Object tag)
        {

        }

        /**
         * 下载出错
         * 
         * @param errorType
         * @param task
         */
        public abstract void error(int errorType, DownloadTask task);

        /**
         * 已经下载完成
         * 
         * @param task
         */
        public abstract void completed(DownloadTask task);
    }

    public static class DownloadTask implements Serializable
    {

        private static final long serialVersionUID = 1l;

        private String downloadUrl;// 下载地址

        private String savePath;// 存储路径

        private Object tag;

        private long totalSize;// 总长度

        private long downSize;// 下载长度

        private long lastSize;// 上次下载长度

        private Map<String, String> params;// 下载时附带的参数

        private boolean isPause;

        private boolean isStop;

        private boolean isRunning;

        private List<OnProgressUpdate> mOnProgressUpdates;// 下载整个过程所触发的方法类

        private int priority;

        public String getDownloadUrl()
        {
            return downloadUrl;
        }

        public void setDownloadUrl(String downloadUrl)
        {
            this.downloadUrl = downloadUrl;
        }

        public String getSavePath()
        {
            return savePath;
        }

        public void setSavePath(String savePath)
        {
            this.savePath = savePath;
        }

        public String getCachePath()
        {
            if (getSavePath() != null)
            {
                return CWSys.getAppCacheFile() + File.separator
                        + StringUtils.md5(getSavePath());
            }
            return null;
        }

        public long getTotalSize()
        {
            return totalSize;
        }

        public void setTotalSize(long totalSize)
        {
            this.totalSize = totalSize;
        }

        public long getDownSize()
        {
            return downSize;
        }

        public void setDownSize(long downSize)
        {
            this.downSize = downSize;
        }

        private long getLastSize()
        {
            return lastSize;
        }

        private void setLastSize(long lastSize)
        {
            this.lastSize = lastSize;
        }

        public Map<String, String> getParams()
        {
            return params;
        }

        public void setParams(Map<String, String> params)
        {
            this.params = params;
        }

        public List<OnProgressUpdate> getOnProgressUpdate()
        {
            if (mOnProgressUpdates == null)
            {
                mOnProgressUpdates = new ArrayList<AsyncDownload.OnProgressUpdate>();
            }
            return mOnProgressUpdates;
        }

        public void setOnProgressUpdate(List<OnProgressUpdate> mOnProgressUpdate)
        {
            this.mOnProgressUpdates = mOnProgressUpdate;
        }

        public int getPriority()
        {
            return priority;
        }

        public void setPriority(int priority)
        {
            this.priority = priority;
        }

        public Object getTag()
        {
            return tag;
        }

        public void setTag(Object tag)
        {
            this.tag = tag;
        }

        public boolean isPause()
        {
            return isPause;
        }

        public void pause()
        {
            this.isPause = true;
        }

        public boolean isStop()
        {
            return isStop;
        }

        public void stop()
        {
            this.isStop = true;
        }

        public boolean isRunning()
        {
            return isRunning;
        }

        public void setRunning()
        {
            this.isRunning = true;
        }

    }

    public class DownloadThread
    {
        /**
         * 一个任务线程最多能请求的次数
         */
        public static final int MAX_REQUEST_COUNT = 2;

        private int seconds;

        private int status;// 下载状态

        private Object threadTag;

        private Thread mThread;

        private HttpMethod request;

        // private boolean isDownloadFail;

        private DownloadTask mTask;

        private boolean isWaited;// 任务是否等待完成(true=等待完成,false=正在等待或是无需等待)

        private boolean pause;

        private boolean isStop;

        private int priority;

        private int requestCount;// 一个任务线程最多能请求的次数

        public int getSeconds()
        {
            return seconds;
        }

        public void resetSecond()
        {
            this.seconds = 0;
        }

        /**
         * 设置该线程的请求对象
         * 
         * @param request
         *            {@link HttpMethod}
         */
        public void setRequest(HttpMethod request)
        {
            this.request = request;
        }

        /**
         * 添加秒数
         * 
         * @param second
         *            单位不一定为秒可能为500毫秒,根据线程等待时间计算
         */
        public void addSecond(int second)
        {
            this.seconds += second;
        }

        public Thread getThread()
        {
            if (mThread == null)
            {
                mThread = new Thread()
                {
                    public void run()
                    {
                        AsyncDownload.this.run(DownloadThread.this);
                    }
                };
                if (priority == PRIORITY_HIGHT)
                {
                    android.os.Process
                            .setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
                }
                else if (priority == PRIORITY_MIDDLE)
                {
                    android.os.Process
                            .setThreadPriority(android.os.Process.THREAD_PRIORITY_DEFAULT);
                }
                else
                {
                    android.os.Process
                            .setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
                }
            }
            return mThread;
        }

        public void relaseThread()
        {
            mThread = null;
        }

        public boolean isAlive()
        {
            return mThread == null ? false : mThread.isAlive();
        }

        // public boolean isDownloadFail()
        // {
        // return isDownloadFail;
        // }
        //
        // public void downloadFail()
        // {
        // this.isDownloadFail = true;
        // }

        public Object getThreadTag()
        {
            return threadTag;
        }

        public void setThreadTag(Object threadTag)
        {
            this.threadTag = threadTag;
        }

        public DownloadTask getTask()
        {
            return mTask;
        }

        public void setTask(DownloadTask mTask)
        {
            this.mTask = mTask;
        }

        public boolean isStop()
        {
            return isStop;
        }

        /**
         * 停止
         * 
         */
        public void stop()
        {
            CWLog.e(TAG, "tag:" + threadTag + "      request:" + request);
            isStop = true;
            if (request != null)
            {
                request.abort();
                request.releaseConnection();
            }
        }

        public boolean isPause()
        {
            return pause;
        }

        /**
         * 暂停
         */
        public void pause()
        {
            isWaited = false;
            this.pause = true;
        }

        /**
         * 任务是否正等待完成
         * 
         * @return 任务是否正等待完成
         */
        public boolean isWaited()
        {
            return isWaited;
        }

        // /**
        // * 移除
        // */
        // public void remove()
        // {
        // isDownloading = false;
        // this.pause = true;
        // }

        /**
         * 恢复下载
         * 
         */
        public void resumse()
        {
            this.pause = false;
        }

        public int getPriority()
        {
            return priority;
        }

        public void setPriority(int priority)
        {
            this.priority = priority;
        }

        public void addRequestCount()
        {
            this.requestCount++;
        }

        /**
         * 请求次数
         * 
         * @return 请求次数
         */
        public int getRequestCount()
        {
            return this.requestCount;
        }

    }

}
